package com.dynamic.mybatis.autoconfigure;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.MybatisMapWrapperFactory;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.dynamic.mybatis.core.datasource.DynamicMultipleDataSource;
import com.dynamic.mybatis.core.intercept.DynamicDataSourceHandler;
import com.dynamic.mybatis.core.intercept.DynamicMappedStatementHandler;
import com.dynamic.mybatis.core.intercept.DynamicSqlSessionHandler;
import com.dynamic.mybatis.core.metadata.DynamicConfiguration;
import com.dynamic.mybatis.core.parser.NamespaceHelper;
import com.dynamic.mybatis.core.session.DynamicSqlSessionTemplate;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.BeansException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableConfigurationProperties({DynamicMybatisProperties.class})
//@ConditionalOnSingleCandidate(DataSource.class)

public class DynamicMybatisAutoConfig implements ApplicationContextAware {

    @Bean
    //@ConditionalOnBean(DataSource.class)
    // @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DynamicMybatisProperties properties) throws Exception {
        System.out.println("===sqlSessionFactory===");
        MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        //数据源
        sqlSessionFactoryBean.setDataSource(new DynamicMultipleDataSource());
        //mybatis配置
        sqlSessionFactoryBean.setTypeAliasesPackage(properties.getTypeAliasesPackage());
        sqlSessionFactoryBean.setMapperLocations(properties.resolveMapperLocations());
        //mybatis Configuration 配置
        DynamicConfiguration configuration = properties.getConfiguration();
        sqlSessionFactoryBean.setObjectWrapperFactory(new MybatisMapWrapperFactory());
        sqlSessionFactoryBean.setConfiguration(configuration);
        //设置不生成短id
        configuration.setUseGeneratedShortKey(Boolean.FALSE);
        return sqlSessionFactoryBean.getObject();
    }


    @Bean
    @ConditionalOnBean(SqlSessionFactory.class)
    @ConditionalOnMissingBean
    public SqlSessionTemplate sqlSessionTemplate(DynamicMybatisProperties properties) throws Exception {
        SqlSessionFactory sqlSessionFactory = sqlSessionFactory(properties);
        System.out.println("===sqlSessionTemplate===");
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    @Bean
    @ConditionalOnBean(SqlSessionTemplate.class)
    @ConditionalOnMissingBean
    public DynamicSqlSessionTemplate dynamicSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate, SqlSessionFactory sqlSessionFactory) {
        System.out.println("===dynamicSqlSessionTemplate===");
        return new DynamicSqlSessionTemplate(sqlSessionFactory, sqlSessionTemplate);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        DynamicSqlSessionTemplate dynamicSqlSessionTemplate = applicationContext.getBean(DynamicSqlSessionTemplate.class);
        if (dynamicSqlSessionTemplate == null) {
            System.err.println("dynamicSqlSessionTemplate is null.");
            return;
        }

        //将DynamicSqlSessionIntercept 加入DynamicSqlSessionTemplate
        if (dynamicSqlSessionTemplate.sqlSessionHandlers().size() < 1) {
            Map<String, DynamicSqlSessionHandler> handlers = applicationContext.getBeansOfType(DynamicSqlSessionHandler.class);
            if (handlers != null && handlers.size() > 0) {
                dynamicSqlSessionTemplate.addSqlSessionHandler(handlers.values());
            }
        }

        //将DynamicMappedStatementIntercept 加入DynamicConfigure
        if (dynamicSqlSessionTemplate.getDynamicMappedStatementHelper().mappedStatementHandlers().size() < 1) {
            Map<String, DynamicMappedStatementHandler> handlers = applicationContext.getBeansOfType(DynamicMappedStatementHandler.class);
            if (handlers != null && handlers.size() > 0) {
                DynamicConfiguration dc = (DynamicConfiguration) dynamicSqlSessionTemplate.getSqlSession().getConfiguration();
                dc.addMappedStatementHandler(handlers.values());
            }
        }

        //将DynamicDataSourceHandler 加入DynamicSqlSessionTemplate
        if (dynamicSqlSessionTemplate.getDynamicDataSourceHandlers().size() < 1) {
            Map<String, DynamicDataSourceHandler> handlers = applicationContext.getBeansOfType(DynamicDataSourceHandler.class);
            if (handlers != null && handlers.size() > 0) {
                dynamicSqlSessionTemplate.addDataSourceHandler(handlers.values());
            }
        }

        //设置数据源
        DataSource multipleDataSource = dynamicSqlSessionTemplate.getSqlSession().getConfiguration().getEnvironment().getDataSource();
        DataSource mainDataSource = applicationContext.getBean(DataSource.class);
        Map<String, DataSource> dataSources = applicationContext.getBeansOfType(DataSource.class);
        if (multipleDataSource instanceof DynamicMultipleDataSource) {
            this.addMultipleDataSource((DynamicMultipleDataSource) multipleDataSource, mainDataSource, dataSources);
            //数据源属性初始化
            dynamicSqlSessionTemplate.afterDataSourcePropertiesSet();
        }
    }


    public void addMultipleDataSource(DynamicMultipleDataSource multipleDataSource, DataSource mainDataSource, Map<String, DataSource> dataSources) {
        Map<Object, DataSource> dataSourceHashMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(dataSources)) {
            for (Map.Entry<String, DataSource> entry : dataSources.entrySet()) {
                if (entry.getValue() instanceof DynamicMultipleDataSource) {
                    continue;
                }
                dataSourceHashMap.put(NamespaceHelper.getIdentity(NamespaceHelper.DEFAULT_NAMESPACE, entry.getKey()), entry.getValue());
            }
        }
        multipleDataSource.set(mainDataSource, dataSourceHashMap);
    }
}